/**
 * 
 */
package com.kingpigeon.iotsvrdemo;

import java.io.UnsupportedEncodingException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;

import io.netty.channel.ChannelHandlerContext;
import io.netty.util.AttributeKey;


/**
 * @author xuhw
 *
 */
public class KingPigeonUtils {
	public static byte Delimiter = (byte)0xA5;//the delimiter in RTU message
	public static AttributeKey<String> attributeKeyMAC = AttributeKey.valueOf("MAC");
	public static long lHeartBeatInterval = 30000;//millisecond
	//to save the RTU devices channels 
	//保存RTU设备的通讯链路
	public static Map<String, ChannelHandlerContext> deviceChannels = new LinkedHashMap<>();
    public static final byte RTUCMD70 = (byte)0x70;
    public static final byte RTUCMD71 = (byte)0x71;
    public static final byte RTUCMD72 = (byte)0x72;
    public static final byte RTUCMD73 = (byte)0x73;
    public static final byte RTUCMD80 = (byte)0x80;
    public static final byte RTUCMD81 = (byte)0x81;
    public static final byte RTUCMD82 = (byte)0x82;
    public static final byte RTUCMD83 = (byte)0x83;
    public static final byte RTUCMD84 = (byte)0x84;
    public static final byte RTUCMD85 = (byte)0x85;
    public static final byte RTUCMD86 = (byte)0x86;
    public static final byte RTUCMD87 = (byte)0x87;
    public static final byte RTUCMD88 = (byte)0x88;
    public static final byte RTUCMD89 = (byte)0x89;
    public static final byte RTUCMD8A = (byte)0x8A;
    public static final byte RTUCMD8B = (byte)0x8B;
    public static final byte RTUCMD8C = (byte)0x8C;
    public static final byte RTUCMD8D = (byte)0x8D;
    public static final byte RTUCMD8E = (byte)0x8E;
    public static final byte RTUCMD94 = (byte)0x94;
    public static final byte RTUCMD97 = (byte)0x97;
    public static final byte RTUCMD99 = (byte)0x99;
    public static final byte RTUCMDA0 = (byte)0xA0;
    public static final byte RTUCMDA1 = (byte)0xA1;
    public static final byte RTUCMDA2 = (byte)0xA2;
    public static final byte RTUCMDA3 = (byte)0xA3;
    public static final byte RTUCMD8F = (byte)0x8F;
    public static final byte RTUCMDB0 = (byte)0xB0;
    public static final byte RTUCMDB1 = (byte)0xB1;
    public static final byte RTUCMDB2 = (byte)0xB2;
    public static final byte RTUCMDB3 = (byte)0xB3;
    public static final byte RTUCMDB4 = (byte)0xB4;
    /** 
     * 对数字和字节进行转换。<br> 
     * 基础知识：<br> 
     * 假设数据存储是以大端模式存储的：<br> 
     * byte: 字节类型 占8位二进制 00000000<br> 
     * char: 字符类型 占2个字节 16位二进制 byte[0] byte[1]<br> 
     * int : 整数类型 占4个字节 32位二进制 byte[0] byte[1] byte[2] byte[3]<br> 
     * long: 长整数类型 占8个字节 64位二进制 byte[0] byte[1] byte[2] byte[3] byte[4] byte[5] 
     * byte[6] byte[7]<br> 
     * float: 浮点数(小数) 占4个字节 32位二进制 byte[0] byte[1] byte[2] byte[3]<br> 
     * double: 双精度浮点数(小数) 占8个字节 64位二进制 byte[0] byte[1] byte[2] byte[3] byte[4] 
     * byte[5] byte[6] byte[7]<br> 
     */ 
	/*Function: Parse byte[] array contents according to ASCII code
	 * 功能：按ASCII码解析byte[]数组内容
	 * @param bytes
	 * @return String
	 */
	public static String bytesToAscii(byte[] bytes, int offset, int dataLen) {  
        if ((bytes == null) || (bytes.length == 0) || (offset < 0) || (dataLen <= 0)) {  
            return null;  
        }  
        if ((offset >= bytes.length) || (bytes.length - offset < dataLen)) {  
            return null;  
        } 
        String asciiStr = null;  
        byte[] data = new byte[dataLen];  
        System.arraycopy(bytes, offset, data, 0, dataLen);  
        try {  
            asciiStr = new String(data, "ISO8859-1");  
        } catch (UnsupportedEncodingException e) {
        	e.printStackTrace();
        }  
        return asciiStr;  
    }  
  
    public static String bytesToAscii(byte[] bytes, int dataLen) {  
        return bytesToAscii(bytes, 0, dataLen);  
    }  
  
    public static String bytesToAscii(byte[] bytes) {  
        return bytesToAscii(bytes, 0, bytes.length);  
    }
	public static int bytesToIntLe(byte[] bytes, int offset, int dataLen) {  
        if ((bytes == null) || (bytes.length == 0) || (offset < 0) || (dataLen <= 0)) {  
            throw new RuntimeException("invalid arg");  
        }
        byte[] bytesTmp = new byte[dataLen];
        System.arraycopy(bytes,offset,bytesTmp,0,dataLen);
        return bytesToIntLe(bytesTmp);
	}
    /** 
     * 功能：将一个不长于4字节（含）的字节数组(小端模式)转换为整数。 
     * Function: Convert an array of bytes (small-endian mode) no longer than 4 bytes (inclusive) to an integer.
     * @param bytes 
     *            字节数组 
     * @return 整数 
     */  
    public static int bytesToIntLe(byte[] bytes) {  
        if ((bytes == null) || (bytes.length > 4)) {  
            throw new RuntimeException("invalid arg");  
        }    
        int ret = 0;    
        for (int i = 0; i < bytes.length; ++i) {  
            ret += ((bytes[i] & 0xFF) << i * 8);  
        }    
        return ret;  
    }
    public static int byteToIntLe(byte b) {
        int ret = 0; 
        ret += (b& 0xFF);
        return ret;  
    }
    /**
     * Convert byte[] to hex string.这里我们可以将byte转换成int，然后利用Integer.toHexString(int)来转换成16进制字符串。
     * 0xaa
     * @param src byte[] data
     * @return hex string
     */
    public static String hexStringFromBytes(byte[] byteArray) {
    	if (byteArray == null){
            return null;
        }
        char[] hexArray = "0123456789ABCDEF".toCharArray();
        char[] hexChars = new char[byteArray.length * 2];
        for (int j = 0; j < byteArray.length; j++) {
            int v = byteArray[j] & 0xFF;
            hexChars[j * 2] = hexArray[v >>> 4];
            hexChars[j * 2 + 1] = hexArray[v & 0x0F];
        }
        return new String(hexChars);

    }
    public static  byte[] bytesFromHexString(String hexString) {
    		if (hexString == null || hexString.equals("")) {
	            return null;
	        }

	        hexString = hexString.replace(" ", "");
	        hexString = hexString.toUpperCase();
	        int length = hexString.length() / 2;
	        char[] hexChars = hexString.toCharArray();
	        byte[] d = new byte[length];
	        for (int i = 0; i < length; i++) {
	            int pos = i * 2;
	            d[i] = (byte) (byteFromChar(hexChars[pos]) << 4 | byteFromChar(hexChars[pos + 1]));
	        }
	        return d;
	}
    private static byte byteFromChar(char c) {
    	return (byte) "0123456789ABCDEF".indexOf(c);
    }    
    public static String bytesToHexString(byte[] bytes, int offset, int len) {  
        if (bytes == null)  
            return "null!";  
  
        StringBuilder ret = new StringBuilder(2 * len);  
  
        for (int i = 0; i < len; ++i) {  
            int b = 0xF & bytes[(offset + i)] >> 4;  
            ret.append("0123456789abcdef".charAt(b));  
            b = 0xF & bytes[(offset + i)];  
            ret.append("0123456789abcdef".charAt(b));  
        }  
  
        return ret.toString();  
    }  
  
    public static String bytesToHexString(byte[] bytes, int len) {  
        return ((bytes == null) ? "null!" : bytesToHexString(bytes, 0, len));  
    }  
  
    public static String bytesToHexString(byte[] bytes) {  
        return ((bytes == null) ? "null!" : bytesToHexString(bytes, bytes.length));  
    }  
    /** 
     * 将一个4位字节数组(小端模式)转换为单精度浮点数。
     * convert an array of bytes which is 4 bytes long (little-endian mode) into a float 
     * @param bytes 
     *            字节数组 
     * @return 单精度浮点数 
     */  
    public static float bytes4ToFloatLe(byte[] bytes,int offset) {   
        if ((bytes == null) ) {  
            throw new RuntimeException("invalid arg");  
        }
        String sHexStr = bytesToHexString(bytes, offset+3, 1)+bytesToHexString(bytes, offset+2, 1)+bytesToHexString(bytes, offset+1, 1)+bytesToHexString(bytes, offset, 1);
        Long i = Long.parseLong(sHexStr, 16);
        return Float.intBitsToFloat(i.intValue());
    }
    public static byte[] bMsgPackageWithoutBody(String sCMD)
    {
    	byte[] bMsgBody = null;
    	return bMsgPackage(sCMD,bMsgBody,0);
    }
    public static byte[] bMsgPackage(String sCMD,byte[] bMsgBody,int iBodyLen)
    {
    	/**
    	 字段名	长度（Byte)	数据类型	说明
			起始		1	整型	默认0xA5
			长度		2	整型(小端模式)	从命令开始到检验之前所有数据的长度，具体整型（小端模式，低位在前）类型字段的解包和组包算法参考<8.2整型(小端模式)类型字段解包/组包样例代码>。
			命令		1	整型	本协议命令使用十六进制标示，如“80”表示一个字节的命令0x80，所有命令参见<4.2.命令表>
			总包数	2	整型(小端模式)	
			当前包	2	整型(小端模式)	
			数据包内容	0~N		每个命令对应的报文结构详见下文<5.数据包内容>
			检验		2	整型(小端模式)	对“长度”字段（含）到“检验”字段（不含）的转义前报文数据进行计算得出的校验值，具体检验算法参考<8.5报文检验算法>
			结束		1	整型	默认0xA5
    	 */
    	byte [] bDelimeter = new byte[1];
    	bDelimeter[0]= KingPigeonUtils.Delimiter;
    	short iMsgLen=(short) (1+2+2+iBodyLen);
    	short iTotalPackage=1;
    	short iCurrentPackage=1;
    	byte[] bMsgTmp=new byte[iBodyLen+11];
    	System.arraycopy(bDelimeter, 						0, bMsgTmp, 0, 1);
    	System.arraycopy(shortToBytesLe(iMsgLen), 			0, bMsgTmp, 1, 2);
    	System.arraycopy(bytesFromHexString(sCMD), 			0, bMsgTmp, 1+2, 1);
    	System.arraycopy(shortToBytesLe(iTotalPackage), 	0, bMsgTmp, 1+2+1, 2);
    	System.arraycopy(shortToBytesLe(iCurrentPackage), 	0, bMsgTmp, 1+2+1+2, 2);
    	if(iBodyLen <=0 )
    	{
	    	short iMsgCheck = MsgCheck(bMsgTmp,1,2+1+2+2);
	    	System.arraycopy(shortToBytesLe(iMsgCheck), 		0, bMsgTmp, 1+2+1+2+2, 2);
	    	System.arraycopy(bDelimeter, 						0, bMsgTmp, 1+2+1+2+2+2, 1);
	    }else
    	{
	    	System.arraycopy(bMsgBody, 							0, bMsgTmp, 1+2+1+2+2, iBodyLen);
	    	short iMsgCheck = MsgCheck(bMsgTmp,1,2+1+2+2+iBodyLen);
	    	System.arraycopy(shortToBytesLe(iMsgCheck), 		0, bMsgTmp, 1+2+1+2+2+iBodyLen, 2);
	    	System.arraycopy(bDelimeter, 						0, bMsgTmp, 1+2+1+2+2+iBodyLen+2, 1);
    	}
		return EscapeCharaterConvertSnd(bMsgTmp);    	
    }
    /**
     * 功能：计算输入报文的校验码
     * Function: Calculate the check code of the input message
     * @param bytes
     * @param iOffset
     * @param iLen
     * @return
     */
    public static short MsgCheck(byte[] bytes,int iOffset,int iLen){
    	//累加和取反
    	int i;
    	short nCheck=0;
    	for(i=0;i<iLen;i++)  
    	{
    		nCheck+=bytes[iOffset+i];
    	}
    	return (short)(~nCheck) ;
    }
    /**
     * 功能：将16比特长度的短整型转化为字节数组
     * Function: convert 16-bit length short integer to byte array
     * @param shortValue
     * @return
     */
    public static byte[] shortToBytesLe(short shortValue) {  
        byte[] bytes = new byte[2];  
  
        for (int i = 0; i < bytes.length; ++i) {  
            bytes[i] = (byte) (shortValue >> i * 8 & 0xFF);  
        }
        return bytes;  
    }
    /**
     * 功能：将64比特长度的长整型转化为字节数组
     * Function: convert 64-bit length long integer to byte array
     * @param shortValue
     * @return
     */
    public static byte[] longToBytesLe(long longValue) {  
        byte[] bytes = new byte[8];  
  
        for (int i = 0; i < bytes.length; ++i) {  
            bytes[i] = (byte) (longValue >> i * 8 & 0xFF);  
        }
        return bytes;  
    }
    /**
     * 功能：将32比特长度的长整型转化为字节数组
     * Function: convert 32-bit length integer to byte array
     * @param shortValue
     * @return
     */
    public static byte[] intToBytesLe(int intValue) {  
        byte[] bytes = new byte[4];  
  
        for (int i = 0; i < bytes.length; ++i) {  
            bytes[i] = (byte) (intValue >> i * 8 & 0xFF);  
        }
        return bytes;  
    }
    /**
	 * 报文使用十六进制A5作为起始和结束符，数据包中除起始与结束外，不可以出现0xA5；
	 * 如包内使用时需要转义，0xA5 使用 0xAA 0x05；0x0A使用0xAA 0x0A
	 * @param data
	 * @return
	 */
	public static byte[] EscapeCharaterConvertSnd(byte[] data) {
		int l = data.length;
		int start = 1;
		int end = l-1;
		StringBuilder da = new StringBuilder(hexStringFromBytes(data));
		int lastIndex = -1;
		do {
			lastIndex=-1;
			for (int i = start; i < end; i++) {
				String sub = da.substring(i * 2, (i + 1) * 2);
				if (sub.equals("A5")) {
					lastIndex = i;
					start = i+1;
					end+=1;
					da.replace(i * 2, (i + 1) * 2, "AA05");
					break;
				} else if (sub.equals("AA")) {
					lastIndex = i;
					start = i+1;
					end+=1;
					da.replace(i * 2, (i + 1) * 2, "AA0A");
					break;
				}
			}

		} while (lastIndex != -1);

		return bytesFromHexString(da.toString());
	}
	/**
	 * 报文使用十六进制A5作为起始和结束符，数据包中除起始与结束外，不可以出现0xA5；
	 * 如包内使用时需要转义，0xA5 使用 0xAA 0x05；0x0A使用0xAA 0x0A
	 * @param data
	 * @return
	 */
	public static byte[] EscapeCharaterConvertRecv(byte[] data) {
		int l = data.length;
		int start = 1;
		int end = l-1;
		StringBuilder da = new StringBuilder(hexStringFromBytes(data));
		int lastIndex = -1;
		do {
			lastIndex=-1;
			for (int i = start; i < end; i++) {
				String sub = da.substring(i * 2, (i + 2) * 2);
				if (sub.equals("AA0A")) {
					lastIndex = i;
					start = i-1;
					end-=1;
					da.replace(i * 2, (i + 2) * 2, "AA");
					break;
				} else if (sub.equals("AA05")) {
					lastIndex = i;
					start = i-1;
					end-=1;
					da.replace(i * 2, (i + 2) * 2, "A5");
					break;
				}
			}
			
		} while (lastIndex != -1);
		
		return bytesFromHexString(da.toString());
	}

	public static String GenRandomNumber(int iLen)
	{
		char[] codeSequence = {'1', '2', '3', '4', '5', '6', '7', '8', '9' };
		Random random = new Random();
	    StringBuffer randomCode = new StringBuffer();

	    for (int i = 0; i < iLen; i++) {
	      String strRand = String.valueOf(codeSequence[random.nextInt(codeSequence.length)]);
	      randomCode.append(strRand);
	    }
	    return randomCode.toString();
	}
}
